package com.acme.net;

import com.acme.domain.bank.Bank;
import com.acme.domain.bank.Client;
import com.acme.exceptions.BrokenBankFeedFileException;
import com.acme.exceptions.ClientExistsException;
import com.acme.service.bank.BankStatistics;
import com.acme.service.bank.impl.BankFeedDataReader;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

import static com.acme.net.Util.echo;
import static com.acme.net.Util.sendMessage;

public class BankServer {
    private final Bank bank;

    public BankServer(Bank bank) {
        this.bank = bank;
    }

    public void start() {
        ServerSocket providerSocket = null;
        Socket connection = null;
        ObjectOutputStream out = null;
        ObjectInputStream in = null;

        try {
            // 1. Creating a server socket
            providerSocket = new ServerSocket(2012, 10);
            // 2. Wait for connection
            echo("Waiting for connection");
            // XXX [MY] This loop cannot complete without throwing exception.
            while (true) {
                connection = providerSocket.accept();
                echo(String.format("Connection received from %s:%s",
                        connection.getInetAddress().getHostName(),
                        connection.getPort()));
                // 3. get Input and Output streams
                out = new ObjectOutputStream(connection.getOutputStream());
                out.flush();
                in = new ObjectInputStream(connection.getInputStream());
                sendMessage("server>", out, "Connection successful");
                // 4. The two parts communicate via the input and output streams
                String message = null;
                do {
                    try {
                        message = (String) in.readObject();
                        echo("client>" + message);
                        if (message.equals("-statistics")) {
                            sendMessage("server>", out, new BankStatistics(bank).print());
                        } else if (message.startsWith("-add")) {
                            String[] input = message.split(" ");
                            if (input.length >= 2) {
                                try {
                                    Client client = new BankFeedDataReader().readClientLine(input[1]);
                                    bank.addClient(client);
                                    sendMessage("server>", out, "Client added");
                                } catch (BrokenBankFeedFileException e) {
                                    sendMessage("server>", out, "Wrong feed data format");
                                } catch (ClientExistsException e) {
                                    sendMessage("server>", out, e.getMessage());
                                }
                            }
                        } else if (message.equals("bye")) {
                            sendMessage("server>", out, "bye");
                        }
                    } catch (ClassNotFoundException e) {
                        System.err.println("Data received in unknown format");
                    }
                } while (message != null && !message.equals("bye"));
            }
        } catch (IOException ioException) {
            ioException.printStackTrace();
        } finally {
            // 4: Closing connection
            try {
                // XXX [MY] What happens if "in.close()" throwing exception ? Will "out" stream be closed ?
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
                if (providerSocket != null) {
                    providerSocket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}